///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2003 Intel Corporation 
// All rights reserved. 
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions are met: 
//
// * Redistributions of source code must retain the above copyright notice, 
// this list of conditions and the following disclaimer. 
// * Redistributions in binary form must reproduce the above copyright notice, 
// this list of conditions and the following disclaimer in the documentation 
// and/or other materials provided with the distribution. 
// * Neither name of Intel Corporation nor the names of its contributors 
// may be used to endorse or promote products derived from this software 
// without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////
/*TU*
    
    TombUPnP - a library for developing UPnP applications.
    
    Copyright (C) 2006-2010 Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
    
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License version 2.1 as published by the Free Software Foundation.
    
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
    
    $Id: ssdp_device.c 2081 2010-03-23 20:18:00Z lww $
*/
#ifdef HAVE_CONFIG_H
    #include "autoconfig.h"
#endif


#include "config.h"
#ifdef INCLUDE_DEVICE_APIS
#if EXCLUDE_SSDP == 0
#include <assert.h>
#include <stdio.h>
#include "ssdplib.h"
#include "upnpapi.h"
#include "ThreadPool.h"
#include "httpparser.h"
#include "httpreadwrite.h"
#include "statcodes.h"
#include "unixutil.h"

#ifdef WIN32
 #include <ws2tcpip.h>
 #include <winsock2.h>
#endif

#define MSGTYPE_SHUTDOWN		0
#define MSGTYPE_ADVERTISEMENT	1
#define MSGTYPE_REPLY			2

/************************************************************************
* Function : advertiseAndReplyThread									
*																	
* Parameters:														
*		IN void *data: Structure containing the search request
*
* Description:														
*	This function is a wrapper function to reply the search request 
*	coming from the control point.
*
* Returns: void *
*	always return NULL
***************************************************************************/
void *
advertiseAndReplyThread( IN void *data )
{
    SsdpSearchReply *arg = ( SsdpSearchReply * ) data;

    AdvertiseAndReply( 0, arg->handle,
                       arg->event.RequestType,
                       &arg->dest_addr,
                       arg->event.DeviceType,
                       arg->event.UDN,
                       arg->event.ServiceType, arg->MaxAge );
    free( arg );

    return NULL;
}

/************************************************************************
* Function : ssdp_handle_device_request									
*																	
* Parameters:														
*		IN http_message_t* hmsg: SSDP search request from the control point
*		IN struct sockaddr_in* dest_addr: The address info of control point
*
* Description:														
*	This function handles the search request. It do the sanity checks of
*	the request and then schedules a thread to send a random time reply (
*	random within maximum time given by the control point to reply).
*
* Returns: void *
*	1 if successful else appropriate error
***************************************************************************/
void
ssdp_handle_device_request( IN http_message_t * hmsg,
                            IN struct sockaddr_in *dest_addr )
{
#define MX_FUDGE_FACTOR 10

    int handle;
    struct Handle_Info *dev_info = NULL;
    memptr hdr_value;
    int mx;
    char save_char;
    SsdpEvent event;
    int ret_code;
    SsdpSearchReply *threadArg = NULL;
    ThreadPoolJob job;
    int replyTime;
    int maxAge;

    // check man hdr
    if( httpmsg_find_hdr( hmsg, HDR_MAN, &hdr_value ) == NULL ||
        memptr_cmp( &hdr_value, "\"ssdp:discover\"" ) != 0 ) {
        return;                 // bad or missing hdr
    }
    // MX header
    if( httpmsg_find_hdr( hmsg, HDR_MX, &hdr_value ) == NULL ||
        ( mx = raw_to_int( &hdr_value, 10 ) ) < 0 ) {
        return;
    }
    // ST header
    if( httpmsg_find_hdr( hmsg, HDR_ST, &hdr_value ) == NULL ) {
        return;
    }
    save_char = hdr_value.buf[hdr_value.length];
    hdr_value.buf[hdr_value.length] = '\0';
    ret_code = ssdp_request_type( hdr_value.buf, &event );
    hdr_value.buf[hdr_value.length] = save_char;    // restore
    if( ret_code == -1 ) {
        return;                 // bad ST header
    }

    HandleLock(  );
    // device info
    if( GetDeviceHandleInfo( &handle, &dev_info ) != HND_DEVICE ) {
        HandleUnlock(  );
        return;                 // no info found
    }
    maxAge = dev_info->MaxAge;
    HandleUnlock(  );

    DBGONLY( UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__,
                         "ssdp_handle_device_request with Cmd %d SEARCH\n",
                         event.Cmd );
             UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__,
                         "MAX-AGE     =  %d\n", maxAge );
             UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__,
                         "MX     =  %d\n", event.Mx );
             UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__,
                         "DeviceType   =  %s\n", event.DeviceType );
             UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__,
                         "DeviceUuid   =  %s\n", event.UDN );
             UpnpPrintf( UPNP_PACKET, API, __FILE__, __LINE__,
                         "ServiceType =  %s\n", event.ServiceType ); )

        threadArg =
        ( SsdpSearchReply * ) malloc( sizeof( SsdpSearchReply ) );

    if( threadArg == NULL ) {
        return;
    }
    threadArg->handle = handle;
    threadArg->dest_addr = ( *dest_addr );
    threadArg->event = event;
    threadArg->MaxAge = maxAge;

    TPJobInit( &job, advertiseAndReplyThread, threadArg );
    TPJobSetFreeFunction( &job, ( free_routine ) free );

    //Subtract a percentage from the mx
    //to allow for network and processing delays
    // (i.e. if search is for 30 seconds, 
    //       respond withing 0 - 27 seconds)

    if( mx >= 2 ) {
        mx -= MAXVAL( 1, mx / MX_FUDGE_FACTOR );
    }

    if( mx < 1 ) {
        mx = 1;
    }

    replyTime = rand(  ) % mx;

    TimerThreadSchedule( &gTimerThread, replyTime, REL_SEC, &job,
                         SHORT_TERM, NULL );
}

/************************************************************************
* Function : NewRequestHandler									
*																	
* Parameters:														
*		IN struct sockaddr_in * DestAddr: Ip address, to send the reply.
*		IN int NumPacket: Number of packet to be sent.
*		IN char **RqPacket:Number of packet to be sent.
*
* Description:														
*	This function works as a request handler which passes the HTTP 
*	request string to multicast channel then
*
* Returns: void *
*	1 if successful else appropriate error
***************************************************************************/
static int
NewRequestHandler( IN struct sockaddr_in *DestAddr,
                   IN int NumPacket,
                   IN char **RqPacket )
{
    int ReplySock,
      socklen = sizeof( struct sockaddr_in );
    int NumCopy,
      Index;
    unsigned long replyAddr = inet_addr( LOCAL_HOST );
    int ttl = 4;                //a/c to UPNP Spec

    ReplySock = socket( AF_INET, SOCK_DGRAM, 0 );
    if( ReplySock == UPNP_INVALID_SOCKET ) {
        DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
                             "SSDP_LIB: New Request Handler:"
                             "Error in socket operation !!!\n" ) );

        return UPNP_E_OUTOF_SOCKET;
    }

    setsockopt( ReplySock, IPPROTO_IP, IP_MULTICAST_IF,
                ( char * )&replyAddr, sizeof( replyAddr ) );
    setsockopt( ReplySock, IPPROTO_IP, IP_MULTICAST_TTL,
                ( char * )&ttl, sizeof( int ) );

    for( Index = 0; Index < NumPacket; Index++ ) {
        int rc;

        // The reason to keep this loop is purely historical/documentation,
        // according to section 9.2 of HTTPU spec:
        // 
        // "If a multicast resource would send a response(s) to any copy of the 
        //  request, it SHOULD send its response(s) to each copy of the request 
        //  it receives. It MUST NOT repeat its response(s) per copy of the 
        //  reuqest."
        //  
        // http://www.upnp.org/download/draft-goland-http-udp-04.txt
        //
        // So, NUM_COPY has been changed from 2 to 1.
        NumCopy = 0;
        while( NumCopy < NUM_COPY ) {
            DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
                                 ">>> SSDP SEND >>>\n%s\n",
                                 *( RqPacket + Index ) );
                 )
                rc = sendto( ReplySock, *( RqPacket + Index ),
                             strlen( *( RqPacket + Index ) ),
                             0, ( struct sockaddr * )DestAddr, socklen );
            imillisleep( SSDP_PAUSE );
            ++NumCopy;
        }
    }

    shutdown( ReplySock, SD_BOTH );
    UpnpCloseSocket( ReplySock );
    return UPNP_E_SUCCESS;
}

/************************************************************************
* Function : CreateServiceRequestPacket									
*																	
* Parameters:														
*	IN int msg_type : type of the message ( Search Reply, Advertisement 
*												or Shutdown )
*	IN char * nt : ssdp type
*	IN char * usn : unique service name ( go in the HTTP Header)
*	IN char * location :Location URL.
*	IN int  duration :Service duration in sec.
*	OUT char** packet :Output buffer filled with HTTP statement.
*
* Description:														
*	This function creates a HTTP request packet.  Depending 
*	on the input parameter it either creates a service advertisement
*   request or service shutdown request etc.
*
* Returns: void
*	
***************************************************************************/
void
CreateServicePacket( IN int msg_type,
                     IN char *nt,
                     IN char *usn,
                     IN char *location,
                     IN int duration,
                     OUT char **packet )
{
    int ret_code;
    char *nts;
    membuffer buf;

    //Notf=0 means service shutdown, 
    //Notf=1 means service advertisement, Notf =2 means reply   

    membuffer_init( &buf );
    buf.size_inc = 30;

    *packet = NULL;

    if( msg_type == MSGTYPE_REPLY ) {
        ret_code = http_MakeMessage( &buf, 1, 1,
                                     "R" "sdc" "D" "s" "ssc" "S" "ssc"
                                     "ssc" "A" "c", HTTP_OK,
                                     "CACHE-CONTROL: max-age=", duration,
                                     "EXT:\r\n", "LOCATION: ", location,
                                     "ST: ", nt, "USN: ", usn, 
                                     gUserHTTPHeaders.buf );
        if( ret_code != 0 ) {
            return;
        }
    } else if( msg_type == MSGTYPE_ADVERTISEMENT ||
               msg_type == MSGTYPE_SHUTDOWN ) {
        if( msg_type == MSGTYPE_ADVERTISEMENT ) {
            nts = "ssdp:alive";
        } else                  // shutdown
        {
            nts = "ssdp:byebye";
        }

        // NOTE: The CACHE-CONTROL and LOCATION headers are not present in
        //  a shutdown msg, but are present here for MS WinMe interop.

        ret_code = http_MakeMessage( &buf, 1, 1,
                                     "Q" "sssdc" "sdc" "ssc" "ssc" "ssc"
                                     "S" "ssc" "A" "c", HTTPMETHOD_NOTIFY, "*",
                                     (size_t)1, "HOST: ", SSDP_IP, ":", SSDP_PORT,
                                     "CACHE-CONTROL: max-age=", duration,
                                     "LOCATION: ", location, "NT: ", nt,
                                     "NTS: ", nts, 
                                     "USN: ", usn, gUserHTTPHeaders.buf );
        if( ret_code != 0 ) {
            return;
        }

    } else {
        assert( 0 );            // unknown msg
    }

    *packet = membuffer_detach( &buf ); // return msg

    membuffer_destroy( &buf );

    return;
}

/************************************************************************
* Function : DeviceAdvertisement									
*																	
* Parameters:														
*	IN char * DevType : type of the device
*	IN int RootDev: flag to indicate if the device is root device
*	IN char * nt : ssdp type
*	IN char * usn : unique service name
*	IN char * location :Location URL.
*	IN int  duration :Service duration in sec.
*
* Description:														
*	This function creates the device advertisement request based on 
*	the input parameter, and send it to the multicast channel.
*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
DeviceAdvertisement( IN char *DevType,
                     int RootDev,
                     char *Udn,
                     IN char *Location,
                     IN int Duration )
{
    struct sockaddr_in DestAddr;

    //char Mil_Nt[LINE_SIZE]
    char Mil_Usn[LINE_SIZE];
    char *msgs[3];
    int ret_code;

    DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
                         "In function SendDeviceAdvertisemenrt\n" );
         )

        DestAddr.sin_family = AF_INET;
    DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP );
    DestAddr.sin_port = htons( SSDP_PORT );

    msgs[0] = NULL;
    msgs[1] = NULL;
    msgs[2] = NULL;

    //If deviceis a root device , here we need to 
    //send 3 advertisement or reply
    if( RootDev ) {
        sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn );
        CreateServicePacket( MSGTYPE_ADVERTISEMENT, "upnp:rootdevice",
                             Mil_Usn, Location, Duration, &msgs[0] );
    }
    // both root and sub-devices need to send these two messages
    //

    CreateServicePacket( MSGTYPE_ADVERTISEMENT, Udn, Udn,
                         Location, Duration, &msgs[1] );

    sprintf( Mil_Usn, "%s::%s", Udn, DevType );
    CreateServicePacket( MSGTYPE_ADVERTISEMENT, DevType, Mil_Usn,
                         Location, Duration, &msgs[2] );

    // check error
    if( ( RootDev && msgs[0] == NULL ) ||
        msgs[1] == NULL || msgs[2] == NULL ) {
        free( msgs[0] );
        free( msgs[1] );
        free( msgs[2] );
        return UPNP_E_OUTOF_MEMORY;
    }
    // send packets
    if( RootDev ) {
        // send 3 msg types
        ret_code = NewRequestHandler( &DestAddr, 3, &msgs[0] );
    } else                      // sub-device
    {
        // send 2 msg types
        ret_code = NewRequestHandler( &DestAddr, 2, &msgs[1] );
    }

    // free msgs
    free( msgs[0] );
    free( msgs[1] );
    free( msgs[2] );

    return ret_code;
}

/************************************************************************
* Function : SendReply									
*																	
* Parameters:	
*	IN struct sockaddr_in * DestAddr:destination IP address.
*	IN char *DevType: Device type
*	IN int RootDev: 1 means root device 0 means embedded device.
*	IN char * Udn: Device UDN
*	IN char * Location: Location of Device description document.
*	IN int  Duration :Life time of this device.
*	IN int ByType:
*
* Description:														
*	This function creates the reply packet based on the input parameter, 
*	and send it to the client addesss given in its input parameter DestAddr.
*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
SendReply( IN struct sockaddr_in *DestAddr,
           IN char *DevType,
           IN int RootDev,
           IN char *Udn,
           IN char *Location,
           IN int Duration,
           IN int ByType )
{
    int ret_code;
    char *msgs[2];
    int num_msgs;
    char Mil_Usn[LINE_SIZE];
    int i;

    msgs[0] = NULL;
    msgs[1] = NULL;

    if( RootDev ) {
        // one msg for root device
        num_msgs = 1;

        sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn );
        CreateServicePacket( MSGTYPE_REPLY, "upnp:rootdevice",
                             Mil_Usn, Location, Duration, &msgs[0] );
    } else {
        // two msgs for embedded devices
        num_msgs = 1;

        //NK: FIX for extra response when someone searches by udn
        if( !ByType ) {
            CreateServicePacket( MSGTYPE_REPLY, Udn, Udn, Location,
                                 Duration, &msgs[0] );
        } else {
            sprintf( Mil_Usn, "%s::%s", Udn, DevType );
            CreateServicePacket( MSGTYPE_REPLY, DevType, Mil_Usn,
                                 Location, Duration, &msgs[0] );
        }
    }

    // check error
    for( i = 0; i < num_msgs; i++ ) {
        if( msgs[i] == NULL ) {
            free( msgs[0] );
            return UPNP_E_OUTOF_MEMORY;
        }
    }

    // send msgs
    ret_code = NewRequestHandler( DestAddr, num_msgs, msgs );
    for( i = 0; i < num_msgs; i++ ) {
        if( msgs[i] != NULL )
            free( msgs[i] );
    }

    return ret_code;
}

/************************************************************************
* Function : DeviceReply									
*																	
* Parameters:	
*	IN struct sockaddr_in * DestAddr:destination IP address.
*	IN char *DevType: Device type
*	IN int RootDev: 1 means root device 0 means embedded device.
*	IN char * Udn: Device UDN
*	IN char * Location: Location of Device description document.
*	IN int  Duration :Life time of this device.

* Description:														
*	This function creates the reply packet based on the input parameter, 
*	and send it to the client address given in its input parameter DestAddr.
*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
DeviceReply( IN struct sockaddr_in *DestAddr,
             IN char *DevType,
             IN int RootDev,
             IN char *Udn,
             IN char *Location,
             IN int Duration )
{
    char *szReq[3],
      Mil_Nt[LINE_SIZE],
      Mil_Usn[LINE_SIZE];
    int RetVal;

    szReq[0] = NULL;
    szReq[1] = NULL;
    szReq[2] = NULL;

    // create 2 or 3 msgs

    if( RootDev ) {
        // 3 replies for root device
        strcpy( Mil_Nt, "upnp:rootdevice" );
        sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn );
        CreateServicePacket( MSGTYPE_REPLY, Mil_Nt, Mil_Usn,
                             Location, Duration, &szReq[0] );
    }

    sprintf( Mil_Nt, "%s", Udn );
    sprintf( Mil_Usn, "%s", Udn );
    CreateServicePacket( MSGTYPE_REPLY, Mil_Nt, Mil_Usn,
                         Location, Duration, &szReq[1] );

    sprintf( Mil_Nt, "%s", DevType );
    sprintf( Mil_Usn, "%s::%s", Udn, DevType );
    CreateServicePacket( MSGTYPE_REPLY, Mil_Nt, Mil_Usn,
                         Location, Duration, &szReq[2] );

    // check error

    if( ( RootDev && szReq[0] == NULL ) ||
        szReq[1] == NULL || szReq[2] == NULL ) {
        free( szReq[0] );
        free( szReq[1] );
        free( szReq[2] );
        return UPNP_E_OUTOF_MEMORY;
    }
    // send replies
    if( RootDev ) {
        RetVal = NewRequestHandler( DestAddr, 3, szReq );
    } else {
        RetVal = NewRequestHandler( DestAddr, 2, &szReq[1] );
    }

    // free
    free( szReq[0] );
    free( szReq[1] );
    free( szReq[2] );

    return RetVal;
}

/************************************************************************
* Function : ServiceAdvertisement									
*																	
* Parameters:	
*	IN char * Udn: Device UDN
*	IN char *ServType: Service Type.
*	IN char * Location: Location of Device description document.
*	IN int  Duration :Life time of this device.

* Description:														
*	This function creates the advertisement packet based 
*	on the input parameter, and send it to the multicast channel.

*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
ServiceAdvertisement( IN char *Udn,
                      IN char *ServType,
                      IN char *Location,
                      IN int Duration )
{
    char Mil_Usn[LINE_SIZE];
    char *szReq[1];
    struct sockaddr_in DestAddr;
    int RetVal;

    DestAddr.sin_family = AF_INET;
    DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP );
    DestAddr.sin_port = htons( SSDP_PORT );

    sprintf( Mil_Usn, "%s::%s", Udn, ServType );

    //CreateServiceRequestPacket(1,szReq[0],Mil_Nt,Mil_Usn,
    //Server,Location,Duration);
    CreateServicePacket( MSGTYPE_ADVERTISEMENT, ServType, Mil_Usn,
                         Location, Duration, &szReq[0] );
    if( szReq[0] == NULL ) {
        return UPNP_E_OUTOF_MEMORY;
    }

    RetVal = NewRequestHandler( &DestAddr, 1, szReq );

    free( szReq[0] );
    return RetVal;
}

/************************************************************************
* Function : ServiceReply									
*																	
* Parameters:	
*	IN struct sockaddr_in *DestAddr:
*	IN char * Udn: Device UDN
*	IN char *ServType: Service Type.
*	IN char * Location: Location of Device description document.
*	IN int  Duration :Life time of this device.

* Description:														
*	This function creates the advertisement packet based 
*	on the input parameter, and send it to the multicast channel.

*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
ServiceReply( IN struct sockaddr_in *DestAddr,
              IN char *ServType,
              IN char *Udn,
              IN char *Location,
              IN int Duration )
{
    char Mil_Usn[LINE_SIZE];
    char *szReq[1];
    int RetVal;

    szReq[0] = NULL;

    sprintf( Mil_Usn, "%s::%s", Udn, ServType );

    CreateServicePacket( MSGTYPE_REPLY, ServType, Mil_Usn,
                         Location, Duration, &szReq[0] );
    if( szReq[0] == NULL ) {
        return UPNP_E_OUTOF_MEMORY;
    }

    RetVal = NewRequestHandler( DestAddr, 1, szReq );

    free( szReq[0] );
    return RetVal;
}

/************************************************************************
* Function : ServiceShutdown									
*																	
* Parameters:	
*	IN char * Udn: Device UDN
*	IN char *ServType: Service Type.
*	IN char * Location: Location of Device description document.
*	IN int  Duration :Service duration in sec.

* Description:														
*	This function creates a HTTP service shutdown request packet 
*	and sent it to the multicast channel through RequestHandler.
*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
ServiceShutdown( IN char *Udn,
                 IN char *ServType,
                 IN char *Location,
                 IN int Duration )
{
    char Mil_Usn[LINE_SIZE];
    char *szReq[1];
    struct sockaddr_in DestAddr;
    int RetVal;

    DestAddr.sin_family = AF_INET;
    DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP );
    DestAddr.sin_port = htons( SSDP_PORT );

    //sprintf(Mil_Nt,"%s",ServType);
    sprintf( Mil_Usn, "%s::%s", Udn, ServType );
    //CreateServiceRequestPacket(0,szReq[0],Mil_Nt,Mil_Usn,
    //Server,Location,Duration);
    CreateServicePacket( MSGTYPE_SHUTDOWN, ServType, Mil_Usn,
                         Location, Duration, &szReq[0] );
    if( szReq[0] == NULL ) {
        return UPNP_E_OUTOF_MEMORY;
    }
    RetVal = NewRequestHandler( &DestAddr, 1, szReq );

    free( szReq[0] );
    return RetVal;
}

/************************************************************************
* Function : DeviceShutdown									
*																	
* Parameters:	
*	IN char *DevType: Device Type.
*	IN int RootDev:1 means root device.
*	IN char * Udn: Device UDN
*	IN char * Location: Location URL
*	IN int  Duration :Device duration in sec.
*
* Description:														
*	This function creates a HTTP device shutdown request packet 
*	and sent it to the multicast channel through RequestHandler.
*
* Returns: int
*	UPNP_E_SUCCESS if successful else appropriate error
***************************************************************************/
int
DeviceShutdown( IN char *DevType,
                IN int RootDev,
                IN char *Udn,
                IN char *_Server,
                IN char *Location,
                IN int Duration )
{
    struct sockaddr_in DestAddr;
    char *msgs[3];
    char Mil_Usn[LINE_SIZE];
    int ret_code;

    msgs[0] = NULL;
    msgs[1] = NULL;
    msgs[2] = NULL;

    DestAddr.sin_family = AF_INET;
    DestAddr.sin_addr.s_addr = inet_addr( SSDP_IP );
    DestAddr.sin_port = htons( SSDP_PORT );

    // root device has one extra msg
    if( RootDev ) {
        sprintf( Mil_Usn, "%s::upnp:rootdevice", Udn );
        CreateServicePacket( MSGTYPE_SHUTDOWN, "upnp:rootdevice",
                             Mil_Usn, Location, Duration, &msgs[0] );
    }

    DBGONLY( UpnpPrintf( UPNP_INFO, SSDP, __FILE__, __LINE__,
                         "In function DeviceShutdown\n" ); )
        // both root and sub-devices need to send these two messages
        CreateServicePacket( MSGTYPE_SHUTDOWN, Udn, Udn,
                             Location, Duration, &msgs[1] );

    sprintf( Mil_Usn, "%s::%s", Udn, DevType );
    CreateServicePacket( MSGTYPE_SHUTDOWN, DevType, Mil_Usn,
                         Location, Duration, &msgs[2] );

    // check error
    if( ( RootDev && msgs[0] == NULL ) ||
        msgs[1] == NULL || msgs[2] == NULL ) {
        free( msgs[0] );
        free( msgs[1] );
        free( msgs[2] );
        return UPNP_E_OUTOF_MEMORY;
    }
    // send packets
    if( RootDev ) {
        // send 3 msg types
        ret_code = NewRequestHandler( &DestAddr, 3, &msgs[0] );
    } else                      // sub-device
    {
        // send 2 msg types
        ret_code = NewRequestHandler( &DestAddr, 2, &msgs[1] );
    }

    // free msgs
    free( msgs[0] );
    free( msgs[1] );
    free( msgs[2] );

    return ret_code;
}

#endif // EXCLUDE_SSDP
#endif // INCLUDE_DEVICE_APIS
